pub trait Builder {
    fn build(&self, pattern: &str, app: &str) -> String;
}

pub enum BuilderType {
    SimpleV1,
    SimpleV2,
}

impl BuilderType {
    pub fn get_builder(&self) -> Box<dyn Builder>
    {
        match self {
            BuilderType::SimpleV1 => Box::new(SimpleBuilderV1),
            BuilderType::SimpleV2 => Box::new(SimpleBuilderV2),
        }
    }
}

struct SimpleBuilderV1;
impl SimpleBuilderV1 {
    fn conv_char(c: u8, w: u8, len: &mut usize) -> char {
        let mut c = c;
        match c {
            b'0'..=b'9' => {
                c = (c + w) % 10 + b'0';
                *len += 1;
            }
            b'A'..=b'Z' => {
                c = (c + w) % 26 + b'A';
                *len += 1;
            }
            b'a'..=b'z' => {
                c = (c + w) % 26 + b'a';
                *len += 1;
            }
            _ => {}
        }

        c as char
    }
}

impl Builder for SimpleBuilderV1 {
    fn build(&self, pattern: &str, app: &str) -> String {
        let mut index = 0;
        let mut result = String::new();
        let app_name_bytes = app.as_bytes();
        for &f in pattern.as_bytes() {
            let &w = app_name_bytes.get(index % app_name_bytes.len()).unwrap();
            result.push(Self::conv_char(f, w, &mut index));
        }

        return result;
    }
}

struct SimpleBuilderV2;
impl SimpleBuilderV2 {
    fn conv(c: u8, w: u8) -> u8 {
        match c {
            b'0'..=b'9' => {
                (c + w) % 10 + b'0'
            }
            b'A'..=b'Z' => {
                (c + w) % 26 + b'A'
            }
            b'a'..=b'z' => {
                (c + w) % 26 + b'a'
            }
            _ => c
        }
    }
}

impl Builder for SimpleBuilderV2 {
    fn build(&self, pattern: &str, app: &str) -> String {
        let mut pattern_vec = pattern.as_bytes().to_vec();
        let mut app_vec = app.as_bytes().to_vec();
        if pattern_vec.len() > app_vec.len() {
            let diff = pattern_vec.len() - app_vec.len();
            for i in 0..diff {
                let c = app_vec.get(i % app_vec.len()).unwrap();
                app_vec.push(*c);
            }
        }

        let mut index = 0;
        app_vec.reverse();
        while !app_vec.is_empty() {
            index = index % pattern_vec.len();
            let w = app_vec.pop().unwrap();
            let c = pattern_vec.get_mut(index).unwrap();
            *c = Self::conv(*c, w);
            index = index + 1;
        }

        pattern_vec.iter().map(|&c| c as char).collect()
    }
}
